package com.radiant.data.annotation;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.radiant.data.entity.GenericDAO;
import com.radiant.data.entity.WallEntity;
import com.radiant.exceptions.RadiantRuntimeException;

public class PreProcessorImpl implements PreProcessor {
	
	private List<Class<?>> listOfClasses = new ArrayList<Class<?>>();

	private Map<Class<?>, Map<String, Boolean>> complexValueMap = 
			new HashMap<Class<?>, Map<String, Boolean>>();

	private Map<Class<?>, Map<String, Boolean>> multiValueMap = 
			new HashMap<Class<?>, Map<String, Boolean>>();
	
	private Map<Class<?>, Map<String, Class<?>>> multiValueTypeMap = 
			new HashMap<Class<?>, Map<String, Class<?>>>();

	private Map<Class<?>, Map<String, Method>> readMethodMap = 
			new HashMap<Class<?>, Map<String, Method>>();
	
	private Map<Class<?>, Map<String, String>> attribToFieldNameMap = 
			new HashMap<Class<?>, Map<String, String>>();

	private Map<Class<?>, Method> itemMethodMap = 
			new HashMap<Class<?>, Method>();
	
	private Map<Class<?>, String> itemNameMap = 
			new HashMap<Class<?>, String>();

	private Map<Class<?>, Class<? extends GenericDAO<?>>> DAOMap = 
			new HashMap<Class<?>, Class<? extends GenericDAO<?>>>();

	/* (non-Javadoc)
	 * @see com.radiant.data.annoation.PreProcessorInterface#register(java.lang.Class<?>)
	 */
	public void register(Class<?> clazz, Class<? extends GenericDAO<?>> daoClazz) {
		listOfClasses.add(clazz);
		DAOMap.put(clazz, daoClazz);
	}

	/* (non-Javadoc)
	 * @see com.radiant.data.annoation.PreProcessorInterface#run()
	 */
	public void run() {
		for (Class<?> clazz: listOfClasses) {
			if (clazz.isAnnotationPresent(Entity.class)) {
				try {
					complexValueMap.put(clazz, new HashMap<String, Boolean>());
					multiValueMap.put(clazz, new HashMap<String, Boolean>());
					readMethodMap.put(clazz, new HashMap<String, Method>());
					multiValueTypeMap.put(clazz, new HashMap<String, Class<?>>());
					attribToFieldNameMap.put(clazz, new HashMap<String, String>());
					registerFields(clazz, clazz, clazz.getDeclaredFields());
					if (clazz.getSuperclass() == WallEntity.class) {
						registerFields(clazz, clazz.getSuperclass(), clazz.getSuperclass().getDeclaredFields());
					}
				} catch (Exception e) {
					throw new RadiantRuntimeException("Error pre-processing class: " + clazz, e);
				}
			}
		}
	}
	
	private void registerFields(Class<?> clazz, Class<?> pdClazz, Field[] fields) throws IntrospectionException {
		for (Field field: fields) {
			if (field.isAnnotationPresent(Persist.class)) {
				Persist p = field.getAnnotation(Persist.class);
				PropertyDescriptor pd = new PropertyDescriptor(field.getName(), pdClazz);
				complexValueMap.get(clazz).put(p.name(), p.complex());
				multiValueMap.get(clazz).put(p.name(), p.multiValue());
				readMethodMap.get(clazz).put(p.name(), pd.getReadMethod());
				attribToFieldNameMap.get(clazz).put(p.name(), field.getName());
				if (p.itemName()) {
					itemMethodMap.put(clazz, pd.getReadMethod());
					itemNameMap.put(clazz, p.name());
				}
				if (p.multiValue()) {
					multiValueTypeMap.get(clazz).put(p.name(), p.multiValueType());
				}
			}
		}

	}
	
	/* (non-Javadoc)
	 * @see com.radiant.data.annoation.PreProcessorInterface#isFieldComplex(java.lang.Class<?>, java.lang.String)
	 */
	public boolean isFieldComplex(Class<?> clazz, String fieldName) {
		Map<String, Boolean> map = complexValueMap.get(clazz);
		if (map != null) {
			return map.get(fieldName).booleanValue();
		} else {
			throw new RadiantRuntimeException ("Class<?>:" + clazz + " not registered");
		}
	}
	
	/* (non-Javadoc)
	 * @see com.radiant.data.annoation.PreProcessorInterface#isFieldMultiValue(java.lang.Class<?>, java.lang.String)
	 */
	public boolean isFieldMultiValue(Class<?> clazz, String fieldName) {
		Map<String, Boolean> map = multiValueMap.get(clazz);
		if (map != null) {
			return map.get(fieldName).booleanValue();
		} else {
			throw new RadiantRuntimeException ("Class<?>:" + clazz + " not registered");
		}		
	}

	/* (non-Javadoc)
	 * @see com.radiant.data.annoation.PreProcessorInterface#getFieldReadMethod(java.lang.Class<?>, java.lang.String)
	 */
	public Method getFieldReadMethod(Class<?> clazz, String fieldName) {
		Map<String, Method> map = readMethodMap.get(clazz);
		if (map != null) {
			return map.get(fieldName);
		} else {
			throw new RadiantRuntimeException ("Class<?>:" + clazz + " not registered");
		}		
	}


	
	public String getFieldNameForAttribName(Class<?> clazz, String attribName) {
		Map<String, String> map = attribToFieldNameMap.get(clazz);
		if (map != null) {
			return map.get(attribName);
		} else {
			throw new RadiantRuntimeException ("Class<?>:" + clazz + " not registered");
		}		
	}

	/* (non-Javadoc)
	 * @see com.radiant.data.annoation.PreProcessorInterface#getItemReadMethod(java.lang.Class<?>)
	 */
	public Method getItemReadMethod(Class<?> clazz) {
		return itemMethodMap.get(clazz);
	}

	public Class<?> getMultiValueComponentType(
			Class<?> clazz, String fieldName) {
		Map<String, Class<?>> map = multiValueTypeMap.get(clazz);
		if (map != null) {
			return map.get(fieldName);
		} else {
			throw new RadiantRuntimeException ("Class<?>:" + clazz + " not registered");
		}		
	}

	public String getItemName(Class<?> clazz) {
		return itemNameMap.get(clazz);
	}
	
	public Class<? extends GenericDAO<?>> getDAOClass(Class<?> clazz) {
		return DAOMap.get(clazz);
	}

}
